/*
 * nPlayer.js, the nPlayer from nTube, designed to combine seperate audio and video streams.
 *
 * version: 1.1.2
 * author: <null> 
 * For the nTube Project. Licensed according to the nLicense.
 */

const nPlayerSyncModes = {
    PERFECT_SYNC: 4,
    LITE_SYNC: 5,
    DISABLED: 3,
    LEGACY_MINIMAL: 2,
    LEGACY_OLD: 1,
    LEGACY: 0,
    /**
     * Unsupported, this will be automatically replaced with PERFECT_SYNC on unsupported browsers.
     * Uses MediaGroups, which is only supported on...uhhh.Safari 10+...nice going there W3C.
     */
    NATIVE: -1,
};
Object.freeze(nPlayerSyncModes);
const nPlayerUtils = {
    needsToSync: function(primaryPlayer, secondaryPlayer, syncMode=nPlayerSyncModes.PERFECT_SYNC, isInBackground=false) {
        if (primaryPlayer == null || secondaryPlayer == null || isInBackground) {
            return false;
        }
        var diff = secondaryPlayer.currentTime - primaryPlayer.currentTime;
        //console.log(diff);
        if (diff < 0) {
            diff /= -1;
        }
        switch (syncMode) {
            case nPlayerSyncModes.PERFECT_SYNC:
                return diff >= 0.12;
            case nPlayerSyncModes.LITE_SYNC:
                return diff >= 0.22;
            case nPlayerSyncModes.DISABLED:
                return false;
            case nPlayerSyncModes.LEGACY_MINIMAL:
                return diff >= 0.45;
            case nPlayerSyncModes.LEGACY_OLD:
                return diff >= 1;
            case nPlayerSyncModes.LEGACY:
                return diff >= 0.6;
            default:
                return false;
        }
    },
    sync: function(primaryPlayer, secondaryPlayer, shouldSync=true, syncMode=nPlayerSyncModes.PERFECT_SYNC, invertSync=false, isInBackground=false) {
        if (primaryPlayer == null || secondaryPlayer == null) {
            return;
        }
        if (shouldSync && this.needsToSync(primaryPlayer, secondaryPlayer, syncMode, isInBackground) && !invertSync) {
            secondaryPlayer.currentTime = primaryPlayer.currentTime;
        }else if (shouldSync && this.needsToSync(primaryPlayer, secondaryPlayer, syncMode, isInBackground) && invertSync) {
            primaryPlayer.currentTime = secondaryPlayer.currentTime;
        }
    },
    backgroundPlaybackSupported: function() {
        try {
            return typeof window.chrome !== "object" && typeof window.chrome.runtime !== "object" && typeof window.chrome.csi !== "function";
        }catch (e) {
            // Assume that window.chrome is undefined
            return true;
        }
    },
    getBackgroundChangeEventName: function() {
        if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support 
            return "visibilitychange";
        } else if (typeof document.msHidden !== "undefined") {
            return "msvisibilitychange";
        } else if (typeof document.webkitHidden !== "undefined") {
            return "webkitvisibilitychange";
        } else {
            return "hidden";
        }
    },
    supportsNativeSync: function() {
        return typeof MediaController !== "undefined";
    },
};
Object.freeze(nPlayerUtils);
const nPlayer = function (displayPlayer, audioPlayer) {
    if (displayPlayer == null || audioPlayer == null) {
        throw new Error("nPlayer: The players cannot be null!");
    }
    var syncMode = nPlayerSyncModes.PERFECT_SYNC;
    var shouldBeSyncing = false;
    var playbackReady = false;
    var programPause = false;
    var backgroundVideoPlayback = nPlayerUtils.backgroundPlaybackSupported();
    var isInBackground = document.hidden && !backgroundVideoPlayback;
    var invSync = false;
    document.addEventListener(nPlayerUtils.getBackgroundChangeEventName(), function() {
        if (backgroundVideoPlayback) {
            isInBackground = false;
            return;
        }
        var curr = document.hidden;
        //console.trace("nPlayer.backgroundHidden: " + curr);
        if (curr && !backgroundVideoPlayback) {
            invSync = false;
            shouldBeSyncing = false;
            displayPlayer.pause();
            //console.trace("nPlayer: pausing displayPlayer... (backgroundNotVisible)");
        }else if (!curr && !backgroundVideoPlayback) {
            invSync = true;
            displayPlayer.play();
            //console.trace("nPlayer: playing displayPlayer... (backgroundNowVisible)");
            if (syncMode != nPlayerSyncModes.NATIVE) {
                shouldBeSyncing = true;
            }
        }
        isInBackground = curr;
    });
    audioPlayer.addEventListener("canplay", function () {
        playbackReady = true;
        displayPlayer.play();
        shouldBeSyncing = (audioPlayer.src != "" && displayPlayer.src != "");
        if (displayPlayer.paused) {
            displayPlayer.play();
        }
	});
	displayPlayer.addEventListener("canplay", function () {
		if (playbackReady) {
            audioPlayer.play();
            shouldBeSyncing = (audioPlayer.src != "" && displayPlayer.src != "");
            playbackReady = true;
            if (audioPlayer.paused) {
                audioPlayer.play();
            }
		}
    });
    displayPlayer.addEventListener("play", function() {
        if (!isInBackground || backgroundVideoPlayback) {
            audioPlayer.play();
            nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, false);
        }
        if (invSync) {
            invSync = false;
        }
        programPause = false;
    });
    audioPlayer.addEventListener("play", function() {
        if (!isInBackground || backgroundVideoPlayback) {
            displayPlayer.play();
            nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, false);
        }
    });
    displayPlayer.addEventListener("timeupdate", function () {
        if (!shouldBeSyncing) {
            return;
        }
        nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
        if (invSync) {
            invSync = false;
        }
	});
	audioPlayer.addEventListener("timeupdate", function () {
        if (!shouldBeSyncing) {
            return;
        }
        nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
        if (invSync) {
            invSync = false;
        }
    });
    displayPlayer.addEventListener("seeking", function () {
        if (!shouldBeSyncing) {
            return;
        }
        nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
        if (invSync) {
            invSync = false;
        }
		if (syncMode != -1) {
            programPause = true;
            audioPlayer.pause();
            shouldBeSyncing = false;		
		}
	});
	displayPlayer.addEventListener("seeked", function () {
        if (!shouldBeSyncing) {
            return;
        }
        if (syncMode != nPlayerSyncModes.NATIVE) {
            shouldBeSyncing = true;
        }
		audioPlayer.play();
		nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
	});
	audioPlayer.addEventListener("seeking", function () {
        if (!shouldBeSyncing) {
            return;
        }
		nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
		if (syncMode != -1) {
            programPause = true;
            displayPlayer.pause();
            shouldBeSyncing = false;	
		}
	});
	audioPlayer.addEventListener("seeked", function () {
        if (!shouldBeSyncing) {
            return;
        }
        if (syncMode != nPlayerSyncModes.NATIVE) {
            shouldBeSyncing = true;
        }
		displayPlayer.play();
		nPlayerUtils.sync(displayPlayer, audioPlayer, shouldBeSyncing, syncMode, invSync, isInBackground);
    });
    displayPlayer.addEventListener("pause", function() {
        if (isInBackground && !backgroundVideoPlayback) {
            return;
        }
        if (!programPause) {
            shouldBeSyncing = false;
        }
        audioPlayer.pause();
    });
    audioPlayer.addEventListener("pause", function() {
        if (!programPause) {
            shouldBeSyncing = false;
        }else {
            displayPlayer.pause();
        }
    });
    if (nPlayerUtils.supportsNativeSync()) {
        syncMode = nPlayerSyncModes.NATIVE;
        shouldBeSyncing = false;
        displayPlayer.mediaGroup = "nPlayer.native";
        audioPlayer.mediaGroup = "nPlayer.native";
    }
    var player = {
        version: "1.1.2",
        play: function() {
            displayPlayer.play();
            audioPlayer.play();
        },
        pause: function() {
            displayPlayer.pause();
            audioPlayer.pause();
        },
        needsToSync: function() {
            var diff = audioPlayer.currentTime - displayPlayer.currentTime;
            if (diff < 0) {
                diff /= -1;
            }
            switch (syncMode) {
                case nPlayerSyncModes.PERFECT_SYNC:
                    return diff >= 0.12;
                case nPlayerSyncModes.LITE_SYNC:
                    return diff >= 0.22;
                case nPlayerSyncModes.DISABLED:
                    return false;
                case nPlayerSyncModes.LEGACY_MINIMAL:
                    return diff >= 0.45;
                case nPlayerSyncModes.LEGACY_OLD:
                    return diff >= 1;
                case nPlayerSyncModes.LEGACY:
                    return diff >= 0.6;
                default:
                    return false;
            }
        },
        sync: function() {
            if (this.needsToSync() && shouldBeSyncing) {
                audioPlayer.currentTime = displayPlayer.currentTime;
            }
        },
        applySource: function(source, source1, ignoreTime=false) {
            if (typeof source !== "string" && typeof source !== "string") {
                throw new Error("nPlayer: One of sources must be a string!")
            }
            if ((source == null && source1 == null) ||
                (source.length == 0 && source1.length == 0)) {
                throw new Error("nPlayer: Both sources cannot be null.")
            }
            var singleSyncMode = (source == null || source1 == null || source.length == 0 || source1.length == 0);
            var displayTime = displayPlayer.currentTime, audioTime = audioPlayer.currentTime;
            shouldBeSyncing = false;
            if (!singleSyncMode) {
                displayPlayer.controls = false;
                displayPlayer.src = source;
                audioPlayer.src = source1;
                if (!ignoreTime) {
                    displayPlayer.currentTime = displayTime;
                    audioPlayer.currentTime = audioTime;
                    shouldBeSyncing = true;
                    displayPlayer.controls = true;
                }else {
                    shouldBeSyncing = true;
                    displayPlayer.controls = true;
                }
            }else {
                displayPlayer.controls = false;
                displayPlayer.src = source;
                audioPlayer.src = "";
                shouldBeSyncing = true;
                if (!ignoreTime) {
                    displayPlayer.currentTime = displayTime;
                    audioPlayer.currentTime = audioTime;
                    shouldBeSyncing = true;
                    displayPlayer.controls = true;
                }else {
                    shouldBeSyncing = true;
                    displayPlayer.controls = true;
                }
            }
            
        },
        setSyncMode: function(mode) {
            if (typeof mode !== "number") {
                throw new Error("nPlayer: This isn't a number!");
            }
            if (mode < 0 || mode > 5) {
                mode = nPlayerSyncModes.PERFECT_SYNC;
            }
            syncMode = mode;
        },
        getSyncMode: function() {
            return syncMode;
        },
        playTill: function(time) {
            throw new Error("nPlayer: Unimpelemented function! [func=playTill]");
        },
        allowBackgroundPlayback: function(value) {
            if (typeof value !== "boolean") {
                throw new Error("nPlayer: Value must be a boolean!");
            }
            backgroundVideoPlayback = value;
        },
        canPlayInBackground: function() {
            return nPlayerUtils.backgroundPlaybackSupported();
        },
        isBackgroundPlaybackAllowed: function() {
            return backgroundVideoPlayback;
        }
    };
    Object.freeze(player);
    return player;
}